{{>partial_header}}
#[allow(unused_imports)]
use std::rc::Rc;
{{^supportAsync}}use std::borrow::Borrow;{{/supportAsync}}
use std::option::Option;

use reqwest;

use crate::apis::ResponseContent;
use super::{Error, configuration};

{{^supportAsync}}
pub struct {{{classname}}}Client {
    configuration: Rc<configuration::Configuration>,
}

impl {{{classname}}}Client {
    pub fn new(configuration: Rc<configuration::Configuration>) -> {{{classname}}}Client {
        {{{classname}}}Client {
            configuration,
        }
    }
}

{{/supportAsync}}
{{#operations}}
{{#operation}}
{{#vendorExtensions.x-group-parameters}}
{{#allParams}}
{{#-first}}
/// struct for passing parameters to the method `{{operationId}}`
#[derive(Clone, Debug)]
pub struct {{{operationIdCamelCase}}}Params {
{{/-first}}
    {{#description}}
    /// {{{.}}}
    {{/description}}
    pub {{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}String{{/isString}}{{#isUuid}}String{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{#hasMore}},{{/hasMore}}
{{#-last}}
}

{{/-last}}
{{/allParams}}
{{/vendorExtensions.x-group-parameters}}
{{/operation}}
{{/operations}}

{{#supportMultipleResponses}}
{{#operations}}
{{#operation}}
/// struct for typed successes of method `{{operationId}}`
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum {{{operationIdCamelCase}}}Success {
    {{#responses}}
    {{#is2xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is2xx}}
    {{#is3xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is3xx}}
    {{/responses}}
    UnknownValue(serde_json::Value),
}

{{/operation}}
{{/operations}}
{{/supportMultipleResponses}}
{{#operations}}
{{#operation}}
/// struct for typed errors of method `{{operationId}}`
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum {{{operationIdCamelCase}}}Error {
    {{#responses}}
    {{#is4xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is4xx}}
    {{#is5xx}}
    Status{{code}}({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/is5xx}}
    {{#isDefault}}
    DefaultResponse({{#isEnum}}{{{enumName}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}),
    {{/isDefault}}
    {{/responses}}
    UnknownValue(serde_json::Value),
}

{{/operation}}
{{/operations}}

{{^supportAsync}}
pub trait {{{classname}}} {
{{#operations}}
{{#operation}}
    {{#vendorExtensions.x-group-parameters}}
    fn {{{operationId}}}(&self{{#allParams}}{{#-first}}, params: {{{operationIdCamelCase}}}Params{{/-first}}{{/allParams}}) -> Result<{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{#returnType}}{{{returnType}}}{{/returnType}}{{/supportMultipleResponses}}, Error<{{{operationIdCamelCase}}}Error>>;
    {{/vendorExtensions.x-group-parameters}}
    {{^vendorExtensions.x-group-parameters}}
    fn {{{operationId}}}(&self, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}&str{{/isString}}{{#isUuid}}&str{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> Result<{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{#returnType}}{{{returnType}}}{{/returnType}}{{/supportMultipleResponses}}, Error<{{{operationIdCamelCase}}}Error>>;
    {{/vendorExtensions.x-group-parameters}}
{{/operation}}
{{/operations}}
}

impl {{{classname}}} for {{{classname}}}Client {
{{/supportAsync}}
{{#operations}}
{{#operation}}
    {{#vendorExtensions.x-group-parameters}}
    {{#supportAsync}}pub async {{/supportAsync}}fn {{{operationId}}}({{^supportAsync}}&self{{/supportAsync}}{{#supportAsync}}configuration: &configuration::Configuration{{/supportAsync}}{{#allParams}}{{#-first}}, params: {{{operationIdCamelCase}}}Params{{/-first}}{{/allParams}}) -> Result<{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{#returnType}}{{{returnType}}}{{/returnType}}{{/supportMultipleResponses}}, Error<{{{operationIdCamelCase}}}Error>> {
        // unbox the parameters
        {{#allParams}}
        let {{paramName}} = params.{{paramName}};
        {{/allParams}}

    {{/vendorExtensions.x-group-parameters}}
    {{^vendorExtensions.x-group-parameters}}
    {{#supportAsync}}pub async {{/supportAsync}}fn {{{operationId}}}({{^supportAsync}}&self{{/supportAsync}}{{#supportAsync}}configuration: &configuration::Configuration{{/supportAsync}}, {{#allParams}}{{{paramName}}}: {{^required}}Option<{{/required}}{{#required}}{{#isNullable}}Option<{{/isNullable}}{{/required}}{{#isString}}&str{{/isString}}{{#isUuid}}&str{{/isUuid}}{{^isString}}{{^isUuid}}{{^isPrimitiveType}}{{^isContainer}}crate::models::{{/isContainer}}{{/isPrimitiveType}}{{{dataType}}}{{/isUuid}}{{/isString}}{{^required}}>{{/required}}{{#required}}{{#isNullable}}>{{/isNullable}}{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> Result<{{#supportMultipleResponses}}ResponseContent<{{{operationIdCamelCase}}}Success>{{/supportMultipleResponses}}{{^supportMultipleResponses}}{{^returnType}}(){{/returnType}}{{#returnType}}{{{returnType}}}{{/returnType}}{{/supportMultipleResponses}}, Error<{{{operationIdCamelCase}}}Error>> {
    {{/vendorExtensions.x-group-parameters}}
        {{^supportAsync}}
        let configuration: &configuration::Configuration = self.configuration.borrow();
        {{/supportAsync}}
        let client = &configuration.client;

        let uri_str = format!("{}{{{path}}}", configuration.base_path{{#pathParams}}, {{{baseName}}}={{#isString}}crate::apis::urlencode({{/isString}}{{{paramName}}}{{^required}}.unwrap(){{/required}}{{#required}}{{#isNullable}}.unwrap(){{/isNullable}}{{/required}}{{#isListContainer}}.join(",").as_ref(){{/isListContainer}}{{#isString}}){{/isString}}{{/pathParams}});
        let mut req_builder = client.{{{httpMethod}}}(uri_str.as_str());

        {{#queryParams}}
        {{#required}}
        req_builder = req_builder.query(&[("{{{baseName}}}", &{{{paramName}}}{{#isListContainer}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isListContainer}}.to_string())]);
        {{/required}}
        {{^required}}
        if let Some(ref s) = {{{paramName}}} {
            req_builder = req_builder.query(&[("{{{baseName}}}", &s{{#isListContainer}}.into_iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","){{/isListContainer}}.to_string())]);
        }
        {{/required}}
        {{/queryParams}}
        {{#hasAuthMethods}}
        {{#authMethods}}
        {{#isApiKey}}
        {{#isKeyInQuery}}
        if let Some(ref apikey) = configuration.api_key {
            let key = apikey.key.clone();
            let val = match apikey.prefix {
                Some(ref prefix) => format!("{} {}", prefix, key),
                None => key,
            };
            req_builder = req_builder.query(&[("{{{keyParamName}}}", val)]);
        }
        {{/isKeyInQuery}}
        {{/isApiKey}}
        {{/authMethods}}
        {{/hasAuthMethods}}
        if let Some(ref user_agent) = configuration.user_agent {
            req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
        }
        {{#hasHeaderParams}}
        {{#headerParams}}
        {{#required}}
        {{^isNullable}}
        req_builder = req_builder.header("{{{baseName}}}", {{{paramName}}}{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
        {{/isNullable}}
        {{#isNullable}}
        match {{{paramName}}} {
            Some(param_value) => { req_builder = req_builder.header("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string()); },
            None => { req_builder = req_builder.header("{{{baseName}}}", ""); },
        }
        {{/isNullable}}
        {{/required}}
        {{^required}}
        if let Some(param_value) = {{{paramName}}} {
            req_builder = req_builder.header("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
        }
        {{/required}}
        {{/headerParams}}
        {{/hasHeaderParams}}
        {{#hasAuthMethods}}
        {{#authMethods}}
        {{#isApiKey}}
        {{#isKeyInHeader}}
        if let Some(ref apikey) = configuration.api_key {
            let key = apikey.key.clone();
            let val = match apikey.prefix {
                Some(ref prefix) => format!("{} {}", prefix, key),
                None => key,
            };
            req_builder = req_builder.header("{{{keyParamName}}}", val);
        };
        {{/isKeyInHeader}}
        {{/isApiKey}}
        {{#isBasic}}
        {{#isBasicBasic}}
        if let Some(ref auth_conf) = configuration.basic_auth {
            req_builder = req_builder.basic_auth(auth_conf.0.to_owned(), auth_conf.1.to_owned());
        };
        {{/isBasicBasic}}
        {{#isBasicBearer}}
        if let Some(ref token) = configuration.bearer_access_token {
            req_builder = req_builder.bearer_auth(token.to_owned());
        };
        {{/isBasicBearer}}
        {{/isBasic}}
        {{#isOAuth}}
        if let Some(ref token) = configuration.oauth_access_token {
            req_builder = req_builder.bearer_auth(token.to_owned());
        };
        {{/isOAuth}}
        {{/authMethods}}
        {{/hasAuthMethods}}
        {{#isMultipart}}
        {{#hasFormParams}}
        let mut form = reqwest::multipart::Form::new();
        {{#formParams}}
        {{#isFile}}
        {{^supportAsync}}
        {{#required}}
        {{^isNullable}}
        form = form.file("{{{baseName}}}", {{{paramName}}})?;
        {{/isNullable}}
        {{#isNullable}}
        match {{{paramName}}} {
            Some(param_value) => { form = form.file("{{{baseName}}}", param_value)?; },
            None => { unimplemented!("Required nullable form file param not supported"); },
        }
        {{/isNullable}}
        {{/required}}
        {{^required}}
        if let Some(param_value) = {{{paramName}}} {
            form = form.file("{{{baseName}}}", param_value)?;
        }
        {{/required}}
        {{/supportAsync}}
        {{#supportAsync}}
        // TODO: support file upload for '{{{baseName}}}' parameter
        {{/supportAsync}}
        {{/isFile}}
        {{^isFile}}
        {{#required}}
        {{^isNullable}}
        form = form.text("{{{baseName}}}", {{{paramName}}}{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
        {{/isNullable}}
        {{#isNullable}}
        match {{{paramName}}} {
            Some(param_value) => { form = form.text("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string()); },
            None => { form = form.text("{{{baseName}}}", ""); },
        }
        {{/isNullable}}
        {{/required}}
        {{^required}}
        if let Some(param_value) = {{{paramName}}} {
            form = form.text("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
        }
        {{/required}}
        {{/isFile}}
        {{/formParams}}
        req_builder = req_builder.multipart(form);
        {{/hasFormParams}}
        {{/isMultipart}}
        {{^isMultipart}}
        {{#hasFormParams}}
        let mut form_params = std::collections::HashMap::new();
        {{#formParams}}
        {{#isFile}}
        {{#required}}
        {{^isNullable}}
        form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content"));
        {{/isNullable}}
        {{#isNullable}}
        match {{{paramName}}} {
            Some(param_value) => { form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content")); },
            None => { unimplemented!("Required nullable file form param not supported with x-www-form-urlencoded content"); },
        }
        {{/isNullable}}
        {{/required}}
        {{^required}}
        if let Some(param_value) = {{{paramName}}} {
            form_params.insert("{{{baseName}}}", unimplemented!("File form param not supported with x-www-form-urlencoded content"));
        }
        {{/required}}
        {{/isFile}}
        {{^isFile}}
        {{#required}}
        {{^isNullable}}
        form_params.insert("{{{baseName}}}", {{{paramName}}}{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
        {{/isNullable}}
        {{#isNullable}}
        match {{{paramName}}} {
            Some(param_value) => { form_params.insert("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string()); },
            None => { form_params.insert("{{{baseName}}}", ""); },
        }
        {{/isNullable}}
        {{/required}}
        {{^required}}
        if let Some(param_value) = {{{paramName}}} {
            form_params.insert("{{{baseName}}}", param_value{{#isListContainer}}.join(","){{/isListContainer}}.to_string());
        }
        {{/required}}
        {{/isFile}}
        {{/formParams}}
        req_builder = req_builder.form(&form_params);
        {{/hasFormParams}}
        {{/isMultipart}}
        {{#hasBodyParam}}
        {{#bodyParams}}
        req_builder = req_builder.json(&{{{paramName}}});
        {{/bodyParams}}
        {{/hasBodyParam}}

        let req = req_builder.build()?;
        let {{^supportAsync}}mut {{/supportAsync}}resp = client.execute(req){{#supportAsync}}.await{{/supportAsync}}?;

        let status = resp.status();
        let content = resp.text(){{#supportAsync}}.await{{/supportAsync}}?;

        if status.is_success() {
            {{^supportMultipleResponses}}
            {{^returnType}}
            Ok(())
            {{/returnType}}
            {{#returnType}}
            serde_json::from_str(&content).map_err(Error::from)
            {{/returnType}}
            {{/supportMultipleResponses}}
            {{#supportMultipleResponses}}
            let entity: Option<{{{operationIdCamelCase}}}Success> = serde_json::from_str(&content).ok();
            let result = ResponseContent { status, content, entity };
            Ok(result)
            {{/supportMultipleResponses}}
        } else {
            let entity: Option<{{{operationIdCamelCase}}}Error> = serde_json::from_str(&content).ok();
            let error = ResponseContent { status, content, entity };
            Err(Error::ResponseError(error))
        }
    }

{{/operation}}
{{/operations}}
{{^supportAsync}}
}
{{/supportAsync}}
